//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in August, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Drawing;

using MapWindow.Geometries;
using MapWindow.Components.Legend;
using MapWindow.Data;
using MapWindow.Main;
using MapWindow.Main.EventArguments;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MapWindow.Drawing
{
    /// <summary>
    /// This is should not be instantiated because it cannot in itself perform the necessary functions.
    /// Instead, most of the specified functionality must be implemented in the more specific classes.
    /// This is also why there is no direct constructor for this class.  You can use the static
    /// "FromFile" or "FromFeatureLayer" to create MapFeatureLayers from a file.
    /// </summary>
    public class MapFeatureLayer : Object, IMapFeatureLayer
    {
        #region Variables

        /// <summary>
        /// Feature Layer 
        /// </summary>
        protected IFeatureLayer _FeatureLayer;
        
        /// <summary>
        /// FeatureSymbolizer for default characteristics for this layer
        /// </summary>
        protected IFeatureSymbolizer _FeatureSymbolizer;
        
        /// <summary>
        /// Boolean, cancels any drawing methods
        /// </summary>
        protected bool _CancelDrawing;
        
        /// <summary>
        /// Boolean, true if actively drawing
        /// </summary>
        protected bool _IsBusy;
        
        
        /// <summary>
        /// list of child legend items
        /// </summary>
        protected ArrayList _LegendItems;
        
        /// <summary>
        /// The text to appear in the legend
        /// </summary>
        protected string _LegendText;



        private Matrix _WorldTransform;
       

       

       
        #endregion

        #region Constructors

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="featureLayer">The data bearing layer to apply new drawing characteristics to</param>
        public MapFeatureLayer(IFeatureLayer featureLayer)
        {
            _FeatureLayer = featureLayer;
            _FeatureSymbolizer = new FeatureSymbolizer(); // just in case we don't override the definition
            _LegendText = Name;
        }

        #endregion



        #region Methods

        /// <summary>
        /// Clears the drawing characteristics from this layer
        /// </summary>
        public virtual void Clear()
        {
            FeatureSymbolizer.Clear();
        }

        /// <summary>
        /// Causes the layer to dispose unmannaged memory objects like Textures, Sprites, Meshes, Index or Vertex buffers
        /// </summary>
        public virtual void Dispose()
        {
             
        }

        /// <summary>
        /// Instructs the layer to draw its contents to the specified Microsoft.DirectX.Direct3D.Device
        /// </summary>
        /// <param name="device">A Microsoft.DirectX.Direct3D.Device to draw to</param>
        public virtual void Draw(Device device)
        {
            throw new NotImplementedException("The drawing code should be implemented in the type specific classes, not inherited.");
        }
       


        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  No changes
        /// will be made to the graphics object for scaling or positioning and it is
        /// assumed that any clip-handling has been handled beforehand.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        public virtual void Draw(System.Drawing.Graphics g)
        {
            Draw(g, Rectangle.Empty, Rectangle.Empty);
        }

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  If the
        /// clipRectangle doesn't intersect the target, no drawing will take place.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        public virtual void Draw(System.Drawing.Graphics g, Rectangle clipRectangle)
        {
            Draw(g, clipRectangle, Rectangle.Empty);
        }

        

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  If the
        /// clipRectangle doesn't intersect the target, no drawing will take place.
        /// The renderable item will be stretched and scaled to fit in the specified target.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        /// <param name="target">This specifies the pixel "Fit To" rectangle</param>
        public virtual void Draw(System.Drawing.Graphics g, Rectangle clipRectangle, Rectangle target)
        {
            throw new InvalidOperationException("Draw should be overridden by the specific subclass");
        }
        // Since the actual drawing code will be handled in a subclass, this can be abstract


        

        /// <summary>
        /// Saves the information in the Layers provided by this datasource onto its existing file location
        /// </summary>
        public virtual void Save()
        {
            _FeatureLayer.Save();
        }

        /// <summary>
        /// Saves a datasource to the file.
        /// </summary>
        /// <param name="Filename">The string filename location to save to</param>
        /// <param name="Overwrite">Boolean, if this is true then it will overwrite a file of the existing name.</param>
        public virtual void SaveAs(string Filename, bool Overwrite)
        {
            _FeatureLayer.SaveAs(Filename, Overwrite);
        }

       

        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets the default color of the border, or outline for the points in this layer.
        /// This will not overwrite characteristics for individual points that have been colored.
        /// Setting this property will also change the BorderPen property
        /// </summary>
        public virtual System.Drawing.Color BorderColor
        {
            get { return _FeatureSymbolizer.BorderColor; }
            set { _FeatureSymbolizer.BorderColor = value; }
        }


        /// <summary>
        /// Gets or sets the default float thickness for the borders of points in this layer.
        /// Setting this property will also affect the BorderPen property.
        /// </summary>
        public virtual float BorderWidth
        {
            get { return _FeatureSymbolizer.BorderWidth; }
            set { _FeatureSymbolizer.BorderWidth = value; }
        }

        /// <summary>
        /// Gets or sets the pen to use with the default points in this layer to draw the borders.
        /// Setting this will also reset the BorderColor proprety.
        /// </summary>
        public virtual System.Drawing.Pen BorderPen
        {
            get { return _FeatureSymbolizer.BorderPen; }
            set { _FeatureSymbolizer.BorderPen = value; }
        }


        /// <summary>
        /// Gets or sets a boolean value that determines whether or not the default points for this layer
        /// will draw a border.  If this property is set to false, the other border properties will be ignored.
        /// </summary>
        public virtual bool BorderVisible
        {
            get { return _FeatureSymbolizer.BorderVisible; }
            set { _FeatureSymbolizer.BorderVisible = value; }
        }

        /// <summary>
        /// This propogates the cancel message down through the drawing levels
        /// to ensure that the current drawing action is terminated as soon
        /// as possible in order to exit gracefully.
        /// </summary>
        public virtual bool CancelDrawing
        {
            get { return _CancelDrawing; }
            set { _CancelDrawing = value; }
            
        }

        /// <summary>
        /// Gets the data source that is associated with this feature layer.
        /// This is read only to prevent confusion since a datasource would
        /// have to perform actions internally to add a new layer.
        /// </summary>
        public virtual IFeatureDataSource DataSource
        {
            get { return _FeatureLayer.DataSource; }
        }

        /// <summary>
        /// Gets the DataTable associated with this specific feature.
        /// </summary>
        public virtual System.Data.DataTable DataTable
        {
            get { return _FeatureLayer.DataTable; }
        }

        /// <summary>
        /// Returns the valid IEnvelope that describes a bounding box that completely contains
        /// this MapFeatureLayer
        /// </summary>
        public virtual IEnvelope Envelope
        {
            get { return _FeatureLayer.Envelope; }
        }

       

        /// <summary>
        /// Gets or sets the list of all the features that are included in this layer.
        /// </summary>
        public virtual List<IFeature> Features
        {
            get { return _FeatureLayer.Features; }
        }

        /// <summary>
        /// Gets or sets and interface for the shared symbol characteristics between point, line and polygon features
        /// </summary>
        public virtual IFeatureSymbolizer FeatureSymbolizer
        {
            get { return _FeatureSymbolizer; }
            set { _FeatureSymbolizer = value; }
        }

        /// <summary>
        /// Specifies Line, Point or Polygon
        /// </summary>
        public virtual FeatureTypes FeatureType
        {
            get { return _FeatureLayer.FeatureType; }
        }

        /// <summary>
        /// Gets or sets the default System.Drawing.Color to be used when drawing points
        /// </summary>
        public virtual System.Drawing.Color FillColor
        {
            get { return _FeatureSymbolizer.FillColor; }
            set { _FeatureSymbolizer.FillColor = value; }
        }

        /// <summary>
        /// Gets or sets the solid color to be used for filling this specific point.
        /// This will also change the FillBrush to a solid brush of the specified color.
        /// </summary>
        public virtual System.Drawing.Brush FillBrush
        {
            get { return _FeatureSymbolizer.FillBrush; }
            set { _FeatureSymbolizer.FillBrush = value; }
        }


        /// <summary>
        /// Returns a dictionary of the fields.
        /// </summary>
        public virtual Dictionary<string, Field> Fields
        {
            get { return _FeatureLayer.Fields; }
        }

        /// <summary>
        /// Gets the string filename
        /// </summary>
        public virtual string Filename
        {
            get { return _FeatureLayer.Filename; }
        }

       

      


        /// <summary>
        /// Gets a value indicating whether or not the Layer is actively drawing
        /// </summary>
        public virtual bool IsBusy
        {
            get { return _IsBusy; }
        }


        /// <summary>
        /// Gets a boolean that is true if no properties have been set for this symbolizer, and all the properties
        /// will just use default values
        /// </summary>
        public virtual bool IsEmpty
        {
            get { return _FeatureSymbolizer.IsEmpty; }
        }

        
        /// <summary>
        /// Gets or sets the complete list of legend items contained within this legend item
        /// </summary>
        public virtual ArrayList LegendItems
        {
            get { return _LegendItems; }
            set { _LegendItems = value; }
        }

        /// <summary>
        /// The text that will appear in the legend 
        /// </summary>
        public virtual string LegendText
        {
            get { return _LegendText; }
            set { _LegendText = value; }
        }

        /// <summary>
        /// Gets or sets a float value from 0 to 1 where 0 is completely transparent
        /// and 1 is completely solid.  Setting an alpha of a specific feature, like
        /// FillColor, to something less than 255 will control that feature's transparency
        /// without affecting the others.  The final transparency of the feature will
        /// be that alpha multiplied by this value and rounded to the nearest byte.
        /// </summary>
        public virtual float Opacity
        {
            get { return _FeatureSymbolizer.Opacity; }
            set { _FeatureSymbolizer.Opacity = value; }
        }

        /// <summary>
        ///  Gets or sets a string name for this layer.
        /// </summary>
        public virtual string Name
        {
            get
            {
                return _FeatureLayer.Name;
            }
            set
            {
                _FeatureLayer.Name = value;
            }
        }

        /// <summary>
        /// Gets or Sets a ScaleModes enumeration that determines whether non-coordinate drawing
        /// properties like width or size use pixels or world coordinates.  If pixels are
        /// specified, a back transform to approximate pixel sizes is applied to the width values.
        /// This controls the default setting that can be overrided by the property for specific 
        /// MapLineStrings.  The default is Pixel.
        /// </summary>
        public virtual ScaleModes ScaleMode
        {
            get { return _FeatureSymbolizer.ScaleMode; }
            set { _FeatureSymbolizer.ScaleMode = value; }
        }

       


        /// <summary>
        /// If this is false, then the drawing function will not render anything.
        /// Warning!  This will also prevent any execution of calculations that take place
        /// as part of the drawing methods and will also abort the drawing methods of any
        /// sub-members to this IRenderable.
        /// </summary>
        public virtual bool Visible
        {
            get { return _FeatureSymbolizer.Visible; }
            set { _FeatureSymbolizer.Visible = value; }
        }

        /// <summary>
        /// Gets or sets the transform that affects all the members of this layer collection.
        /// </summary>
        public virtual Matrix WorldTransform
        {
            get { return _WorldTransform; }
            set { _WorldTransform = value; }
        }


        #endregion



        #region Events

        #region Paint Events
        /// <summary>
        /// Occures after the paint event is complete.  The "clipRectangle" was actually the 
        /// "Fit-To" rectangle that the frame is drawn within.
        /// </summary>
        public virtual event EventHandler<PaintCompleted> AfterPainting;

        /// <summary>
        /// The drawing method finished, and exited normally.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface used by the draw method.</param>
        /// <param name="clipRect">The System.Drawing.Rectangle defining the area to actually perform drawing</param>
        /// <param name="target">The destination rectangle for the System.Drawing.Rectangle where the result should be drawn</param>
        protected virtual void OnAfterPainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(g, clipRect, target));
            }
            _IsBusy = false;
        }


        /// <summary>
        /// Fires before the paint event.  If the cancel property of this event is set to true,
        /// the paint event will be canceled.  The rect property is the "fit-to" property for
        /// this event.
        /// </summary>
        public virtual event EventHandler<PaintCancel> BeforePainting;

        /// <summary>
        /// Fires the BeforeDrawing event
        /// </summary>
        /// <param name="g"></param>
        /// <param name="clipRect"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        protected virtual bool OnBeforePainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (BeforeDrawing != null)
            {
                PaintCancel e = new PaintCancel(g, clipRect, target);
                BeforePainting(this, e);
                if (e.Cancel == true) return true;
            }
            return false;
        }

       

        /// <summary>
        /// Internal Method to fire the Cancel Event.  This caries the same basic information that the 
        /// DrawingComplete method, plus an exception if any as well as a boolean indicating the draw method was cancelled.
        /// </summary>
        /// <param name="Error">A System.Exception showing the error that caused the cancelation</param>
        /// <param name="g">A System.Drawing.Graphics surface that drawing was taking place on</param>
        /// <param name="clipRect">The ClipRectangle or Rectangle.Empty</param>
        /// <param name="target">The Destination Rectangle or Rectangle.Empty</param>
        protected virtual void OnPaintCancelled(System.Exception Error, System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(Error, true, g, clipRect, target));
            }
            _IsBusy = false;
        }

        #endregion

        #region Draw Events

        /// <summary>
        /// Occurs when the Draw method is called and provides the option to cancel the event before anything is actually drawn.
        /// </summary>
        public virtual event EventHandler<DrawCancel> BeforeDrawing;

        /// <summary>
        /// Fires the event that occurs before drawing to a directX map
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that will be drawn to</param>
        /// <returns>Boolean, that if true should cancel the draw event.</returns>
        protected virtual bool OnBeforeDrawing(Device device)
        {
            if (BeforeDrawing == null) return false;
            DrawCancel e = new DrawCancel(device);
            BeforeDrawing(this, e);
            return e.Cancel;
        }


        /// <summary>
        /// Occurs after the contents of this MapLayerCollection have been rendered to the Microsoft.DirectX.Direct3D.Device
        /// </summary>
        public virtual event EventHandler<DrawCompleted> AfterDrawing;

        /// <summary>
        /// Fires an event when we are done with the drawing code, whether the drawing was successful, cancelled, or threw an exception.
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that rendering took place on</param>
        /// <param name="cancelled">If true, the event was cancelled normally</param>
        /// <param name="ex">An Exception in the event that this was an error.  This will normally be null.</param>
        protected virtual void OnAfterDrawing(Device device, bool cancelled, Exception ex)
        {
            if (AfterDrawing == null) return;
            DrawCompleted e;
            if (ex == null)
            {
                e = new DrawCompleted(device, cancelled);
            }
            else
            {
                e = new DrawCompleted(device, ex);
            }
            AfterDrawing(this, e);
        }


        #endregion

        #endregion


        #region Static Methods

        /// <summary>
        /// The static methods serve as an impromptu factory, handling the typing as necessary
        /// </summary>
        /// <param name="featureLayer">The data layer for a feature layer like a line, point or polygon layer.</param>
        /// <param name="progHandler">An IProgress interface for handling progress messages</param>
        /// <returns></returns>
        public IMapFeatureLayer FromFeatureLayer(IFeatureLayer featureLayer, IProgHandler progHandler)
        {
            if (featureLayer.FeatureType == FeatureTypes.Point)
            {
                return new MapPointLayer(featureLayer, progHandler);
            }
            if (featureLayer.FeatureType == FeatureTypes.Line)
            {
                return new MapLineStringLayer(featureLayer);
            }
            // To Do - MapPolygonLayer
            return null;
        }

        /// <summary>
        /// Creates a new instance of the appropriate subtype of MapFeatureLayer, depending on the
        /// characteristics of the file.  This will return either a MapPointLayer, a MapLineStringLayer
        /// or a MapPolygonLayer.
        /// </summary>
        /// <param name="Filename">The string filename specifying the shapefile or other data source file to open. </param>
        /// <param name="progHandler">A progress handling class</param>
        /// <returns>A valid IMapFeatureLayer interface that can be upcast to the appropriate Point, Line or Polygon type.</returns>
        /// 
        public IMapFeatureLayer FromFile(string Filename, IProgHandler progHandler)
        {
            if (System.IO.Path.GetExtension(Filename) != ".shp")
            {
                throw new NotSupportedException("Only .shp vector feature layers are currently supported.");
            }
            ShapefileFeatureLayer newLayer = new ShapefileFeatureLayer(Filename);
    
            if (newLayer.FeatureType == FeatureTypes.Point)
            {
                return new MapPointLayer(newLayer, progHandler);
            }
            if (newLayer.FeatureType == FeatureTypes.Line)
            {
                return new MapLineStringLayer(newLayer);
            }
            // To Do - MapPolygonLayer
            return null;
        }
        

        #endregion

    }


}
